package com.nrg.richie.server.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.nrg.richie.shared.NodeData;

public class XMLUtils {

	private static Logger logger = Logger.getLogger(XMLUtils.class.getName());
	
	public static Document getXml(InputStream stream) throws ParserConfigurationException, SAXException, IOException {
        
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder docBuilder = dbf.newDocumentBuilder();
        Document xml = docBuilder.parse(stream);

        return xml;
        
    }

	public static String getXmlString(Document document) {
		return getXmlString(new DOMSource(document));
	}
			
	public static String getXmlString(Element element) {
		return getXmlString(new DOMSource(element));
	}

    private static String getXmlString(DOMSource source) {
    	try {
			guessAndSetTransformerFactoryProp();
			
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
			
			StreamResult result = new StreamResult(new StringWriter());
	
			transformer.transform(source, result);
			
			String xmlString = result.getWriter().toString();
			
			return xmlString.replaceAll("\\s+(\\n)\\n*", "$1");
    	} catch (Exception e) {
    		logger.warning(e.getMessage());
			return null;
		}
    }
	
    
    private static boolean tryTransformerFactoryGuess(String guess) {
        try {
          Class.forName(guess);
          System.setProperty("javax.xml.transform.TransformerFactory", guess);
          return true;
        } catch (ClassNotFoundException e) {
          return false;
        }
      }

      /**
       * Some JDKs apparently don't set the TransformerFactory setting to a class
       * that actually exists, so probe a couple of possible values here.
       */
      private static void guessAndSetTransformerFactoryProp() {
        if (tryTransformerFactoryGuess("org.apache.xalan.processor.TransformerFactoryImpl")) {
          return;
        }
        if (tryTransformerFactoryGuess(
            "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl")) {
          return;
        }
        // If we didn't find any, just continue and hope the JDK does the right
        // thing by default.
      }
    
      public static ArrayList<NodeData> processXml(Document document, Locale locale) {
	    	
      	ArrayList<NodeData> NodeDataList = new ArrayList<NodeData>();

      	List<String> cityCodesToCheck = new ArrayList<String>();
      	List<String> propertyTypesToCheck = new ArrayList<String>();   	

      	
  		Properties cityCodes = new Properties();
  		Properties propertyTypeCodes = new Properties();
  		
  		try {			
  			cityCodes.load(XMLUtils.class.getClassLoader().getResourceAsStream("cityCodes.properties"));
  		} catch (Exception e) { 
  			logger.severe(StringUtils.getStacktraceAsString(e));
  		}

  		try {			
  			propertyTypeCodes.load(XMLUtils.class.getClassLoader().getResourceAsStream("propertyTypeCodes.properties"));
  		} catch (Exception e) { 
  			logger.severe(StringUtils.getStacktraceAsString(e));
  		}

      	try {
      		
      		Properties props = new Properties();
  			props.load(XMLUtils.class.getClassLoader().getResourceAsStream("config.properties"));
  			
  			String cityCodesToCheckString = props.getProperty("cityCodesToCheck");
  			if (cityCodesToCheckString != null && cityCodesToCheckString.length() > 0) {
  				cityCodesToCheck.addAll(Arrays.asList(cityCodesToCheckString.split(",")));
  			}

  			String propertyTypesToCheckString = props.getProperty("propertyTypesToCheck");
  			if (propertyTypesToCheckString != null && propertyTypesToCheckString.length() > 0) {
  				propertyTypesToCheck.addAll(Arrays.asList(propertyTypesToCheckString.split(",")));
  			}

  		} catch (Exception e) {
  			logger.severe(StringUtils.getStacktraceAsString(e));
  		}
      	
      	// Generate node list from xml document
//  		NodeList nodes = document.getElementsByTagName("RespoAd");
      	NodeList nodes = document.getElementsByTagName("property");
  		// Loop through user nodes
  		for (int i = 0; i < nodes.getLength(); i++)	{
  			// Reference current element
  			Element element = (Element)nodes.item(i);
  			NodeData nodeData = new NodeData();
  			
//  			Element adElement = (Element)element.getElementsByTagName("Ad").item(0);
//  			
//  			if (adElement != null) {
  //
//  				Element idElement = (Element)adElement.getElementsByTagName("externalid").item(0);
//  				if (idElement != null) {
//  					nodeData.setId(idElement.getTextContent());
//  				}				
  //
//  				Element descriptionElement = (Element)adElement.getElementsByTagName("text").item(0);
//  				if (descriptionElement != null) {
//  					nodeData.setDescription(descriptionElement.getTextContent());
//  				}
//  				
//  				Element priceElement = (Element)adElement.getElementsByTagName("buyprice").item(0);
//  				if (priceElement != null) {
//  					nodeData.setPrice(priceElement.getTextContent());
//  				}
//  			}

  			String id = element.getAttribute("source-system-id");
  			String type = element.getAttribute("property-type");

  			nodeData.setId(id);
  			
  			if (type != null && type.trim().length() > 0) {			
  				nodeData.setType(propertyTypeCodes.getProperty(type));
  			}

  			Element referenceNumberElement = (Element)element.getElementsByTagName("reference-number").item(0);
  			if (referenceNumberElement != null) {
  				nodeData.setReferenceNumber(referenceNumberElement.getTextContent());
  			}
  			
  			Element descriptionElement = (Element)element.getElementsByTagName("description").item(0);
  			if (descriptionElement != null) {
  				String description = descriptionElement.getTextContent();
  				
  				nodeData.setDescription(description);
  				if (description != null && !description.isEmpty()) {
	  		        Pattern p = Pattern.compile("([\\d]{2}/[\\d]{3}-[\\d]{4})");
	  		        Matcher m = p.matcher(description);
	
	  		        if (m.find()) {
	  		        	nodeData.setPhoneNumber(m.group());
	  		        }
  				}  				
  			}

  			Element priceElement = (Element)element.getElementsByTagName("price").item(0);
  			if (priceElement != null) {
  				
  				String priceString = priceElement.getTextContent();
  				nodeData.setPrice(priceString);

  			}
  		
  			Element cityElement = (Element)element.getElementsByTagName("city").item(0);
  			String cityCode = "";
  			if (cityElement != null) {
  				
  				cityCode = cityElement.getTextContent();
  				if (cityCode != null && cityCode.trim().length() > 0) {			
  					nodeData.setCity(cityCodes.getProperty(cityCode));
  				}
  			}			
  			
  			Element zipElement = (Element)element.getElementsByTagName("postal-code").item(0);
  			if (zipElement != null) {
  				nodeData.setZip(zipElement.getTextContent());
  			}			
  						
  			Element streetElement = (Element)element.getElementsByTagName("street").item(0);
  			if (streetElement != null) {
  				nodeData.setStreet(streetElement.getTextContent());
  			}			

  			
  			//preselection checking
  			if (cityCode != null && cityCode.trim().length() > 0 &&
  					type != null && type.trim().length() > 0 &&					
  					propertyTypesToCheck.contains(type) && cityCodesToCheck.contains(cityCode)) {
  				nodeData.setPreSelected(Boolean.TRUE.toString());
  			} else {
  				nodeData.setPreSelected(Boolean.FALSE.toString());
  			}

  			NodeDataList.add(nodeData);
  		}
  		
  		return NodeDataList;
  	}      
      
}
